package com.topsail.demo.context;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Map;
import java.util.Objects;

/**
 * <B>主类名称：</B><BR>
 * <B>概要说明：</B><BR>
 *
 * ProviderContextFilter
 * ConsumerContextFilter
 *
 * @author steven.zhoulin
 *
 * @since 2023年7月10日 15:36
 */
@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER})
public class ContextFilter implements Filter {

    private static final String USER_TOKEN = "crmToken";
    private static final String USER_CONTEXT = "userContext";

    /**
     *
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 只有当 UserContext 不存在时，才尝试去解析 Token
        if (!transportUserContext(invocation)) {
            processUserToken(invocation);
        }

        Result invoke = invoker.invoke(invocation);

        // 清理上下文，确保不污染后续请求
        UserContextHolder.clear();

        return invoke;
    }

    /**
     * 透传用户上下文
     *
     * @param invocation
     * @return
     */
    private boolean transportUserContext(Invocation invocation) {
        UserContext userContext = (UserContext) invocation.getObjectAttachment(USER_CONTEXT);
        if (Objects.nonNull(userContext)) {
            invocation.setAttachment(USER_CONTEXT, userContext);
            UserContextHolder.setUserContext(userContext);
            return true;
        }
        return false;
    }

    /**
     * 解析令牌
     *
     * @param invocation
     * @return
     */
    private boolean processUserToken(Invocation invocation) {
        String crmToken = invocation.getAttachment(USER_TOKEN);

        if (Objects.nonNull(crmToken)) {
            Map<String, Object> claims = JwtUtils.parserJwt(crmToken);

            // 解析 crmToken
            UserContext userContext = new UserContext();
            userContext.setUserId(Long.parseLong(claims.get("userId").toString()));
            userContext.setUserName((String) claims.get("username"));
            invocation.setAttachment(USER_CONTEXT, userContext);
            return true;
        }

        return false;
    }

}
